/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package plugins.lcdstation.io;

import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.UnsupportedCommOperationException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * 
 * @author Hansj�rg
 */
public class ExDisplay {

	static final byte CMD_DATA_ENABLE = 0x03;
	static final byte CMD_DATA_DISABLE = 0x01;
	static final byte CMD_COMMAND_ENABLE = 0x02;
	static final byte CMD_COMMAND_DISABLE = 0x00;
	static final byte CMD_DISPLAY_CLEAR = 0x01;
	static final byte CMD_CURSOR_HOME = 0x02;
	static final byte CMD_CURSOR_RIGHT = 0x14;
	static final byte CMD_ENTRY_MODE = 0x06;
	static final byte CMD_DISPLAY_CONTROL = 0x0E;
	static final byte CMD_DISPLAY_OFF = 0x09;
	static final byte CMD_BACKLIGHT_ON = 0x08;
	static final byte CMD_BACKLIGHT_OFF = 0x00;
	static final byte CMD_FUNCTION_SET = 0x28;
	static final int BAUD_RATE = 9600;
	static final int DATABITS = SerialPort.DATABITS_8;
	static final int STOPBITS = SerialPort.STOPBITS_1;
	static final int PARITY = SerialPort.PARITY_NONE;
	static CommPort commPort;
	static InputStream in;
	static OutputStream out;
	static boolean opened = false;
	static boolean purge_flag = true;
	static boolean backlight = false;

	static int line_index[] = new int[] { 0x00, 0x14, 0x40, 0x54 };

	private ExDisplay() {
	}

	public static void Initialize(String com_port) throws NoSuchPortException,
			PortInUseException, UnsupportedCommOperationException, IOException,
			InterruptedException {
		InitializeCOM(com_port);
		InitializeExDisplay();
	}

	/**
	 * 
	 * @param com_port
	 * @throws NoSuchPortException
	 * @throws PortInUseException
	 * @throws UnsupportedCommOperationException
	 * @throws IOException
	 *  zugang zur COM vorbereiten
	 */
	private static void InitializeCOM(String com_port)
			throws NoSuchPortException, PortInUseException,
			UnsupportedCommOperationException, IOException {
		System.out.println("Initialize connection to external display on COM: "
				+ com_port);
		CommPortIdentifier portIdentifier = CommPortIdentifier
				.getPortIdentifier(com_port);

		if (portIdentifier.isCurrentlyOwned()) {
			System.out.println("    [ERROR]: Recoverable fatal error:");
			System.out.println("    Port already in use!");
			throw new PortInUseException();
		}

		System.out.println("    Open port...");
		CommPort commPort = portIdentifier.open("plugins.lcdstation.ExDisplay",
				2000);

		if (commPort instanceof SerialPort) {
			System.out.println("    [SUCCESS] Port opened!");
			SerialPort serialPort = (SerialPort) commPort;
			serialPort.setSerialPortParams(ExDisplay.BAUD_RATE,
					ExDisplay.DATABITS, ExDisplay.STOPBITS, ExDisplay.PARITY);

			in = serialPort.getInputStream();
			out = serialPort.getOutputStream();

			opened = true;
		} else {
			System.out.println("    [ERROR]: Unknown error:");
			System.out.println("    Port can not be opened!");
		}
	}

	/**
	 * 
	 * @throws IOException
	 * @throws InterruptedException
	 *  Neustart LCD Initzialisieren
	 */
	private static void InitializeExDisplay() throws IOException,
			InterruptedException {
		System.out.println("Initialize external display!");

		int display[] = {0x30, 0x30, 0x30, CMD_FUNCTION_SET, CMD_FUNCTION_SET,
				CMD_DISPLAY_CLEAR, CMD_DISPLAY_CONTROL, CMD_ENTRY_MODE};

		for (int i = 0; i < display.length; ++i) {
			SendCmd(display[i] & 0xf0); 

			if (i > 3) {
				SendCmd((display[i] << 4) & 0xf0); 
			} 
			Thread.sleep(50);

			purge_flag = !purge_flag;
		}
	}

		/**
		 * 
		 * @param display
		 * @throws IOException
		 * @throws InterruptedException
		 * Display zum übergabe von Commando steuern
		 */
	private static void SendCmd(int display) throws IOException,
		InterruptedException {
		int instruktion = ((display & 0xf0) | 0x00 | (backlight ? CMD_BACKLIGHT_OFF : CMD_BACKLIGHT_ON));
		out.write(instruktion);  // LCD E CS =0 R/S =0  Command disable
		out.flush();
		Thread.sleep(10);
		
		instruktion = ((display & 0xf0) | 0x02) | (backlight ? CMD_BACKLIGHT_OFF : CMD_BACKLIGHT_ON);
		out.write(instruktion);  // LCD E CS =1 R/S =0  Command enable
		out.flush();
		Thread.sleep(10);
		
		instruktion = ((display & 0xf0) | 0x00 | (backlight ? CMD_BACKLIGHT_OFF : CMD_BACKLIGHT_ON));
		out.write(instruktion);  // LCD E CS =0 R/S =0  Command disable
		out.flush();
		Thread.sleep(25);
	}
	
	/**
	 * 
	 * @param data
	 * @throws IOException
	 *  Display zum übergabe von Zeichen steuern
	 */
	private static void SendData(int data) throws IOException {

		try {
			int instruktion = ((data & 0xf0) | 0x01 | (backlight ? CMD_BACKLIGHT_OFF : CMD_BACKLIGHT_ON));
			out.write(instruktion);  // LCD E CS =0 R/S =1  Data disable
			out.flush();

			instruktion = ((data & 0xf0) | 0x03 | (backlight ? CMD_BACKLIGHT_OFF : CMD_BACKLIGHT_ON));
			out.write(instruktion);  // LCD E CS =1 R/S =1  Data enable
			out.flush();

			instruktion = ((data & 0xf0) | 0x01 | (backlight ? CMD_BACKLIGHT_OFF : CMD_BACKLIGHT_ON));
			out.write(instruktion);  // LCD E CS =0 R/S =1  Data disable
			out.flush();
			Thread.sleep(15);
			
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
			
	}
	
	/**
	 * 
	 * @param instruktion
	 * @throws IOException
	 * @throws InterruptedException
	 *  2 mal 4Bit High Low an Command übergeben 
	 */
	public static void CMD_Send(int instruktion) throws IOException, InterruptedException {
		SendCmd(instruktion);
		SendCmd(instruktion << 4);
	}
	
	/**
	 * 
	 * @throws IOException
	 *   2 mal 4Bit High, Low Command fest übergeben
	 */
	public static void ClearDisplay() throws IOException {
		try {
			SendCmd(CMD_DISPLAY_CLEAR);
			SendCmd((CMD_DISPLAY_CLEAR) << 4);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 
	 * @param line
	 * @param txt
	 * @throws IOException
	 *  2 mal 4Bit High/Low Eingabedaten Zeichenweise übergeben 
	 *  4 Zeilen a 20 Zeichen
	 */
	public static void PrintText(int line, String txt) throws IOException {
		try {
			int index = line_index[line];
			if (line == 0) {
				ClearDisplay();
				//CMD_Send(index | 0x80);
				//System.out.println("Index: " + (index | 0x80));
			}
				CMD_Send(index | 0x80);

			Thread.sleep(20);

			byte[] ascii = txt.getBytes("US-ASCII");
			
			for (int j = 0; (j < ascii.length) & (j < 20); j++) {
				SendData(ascii[j]);
				SendData(ascii[j] << 4);					// 2 x 4Bit char ausgeben

				//purge_flag = !purge_flag;
			}
			
			int i;
			i = 20;
			if( line == 3) {
				i--;
			}
			
			for (; ascii.length < i ; --i) {		// rest mit Space auffüllen
				SendData(' ');
				SendData(' ' << 4);
			}
			
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 
	 * @param state
	 * @throws IOException
	 *   set BIT 3 an Display zum Backlight on/off schalten
	 */
	public static void SetBacklight(boolean state) throws IOException {
		ExDisplay.backlight = state;

		int instruktion = backlight ? CMD_BACKLIGHT_OFF : CMD_BACKLIGHT_ON;

		//System.out.println("    Sending to external Display: " + instruktion
		//		+ " (Backlight " + (backlight ? "off" : "on") + ")");
		
		out.write(backlight ? CMD_BACKLIGHT_OFF : CMD_BACKLIGHT_ON);
		out.flush();
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

	/**
	 * 
	 * @throws IOException
	 *  Beenden und alles schliesen  
	 */
	public static void Dispose() throws IOException {
		SetBacklight(false);
		ClearDisplay();

		in.close();
		out.close();
	}
}
